package com.iscreen.dbupgrade.commands;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.iscreen.dbupgrade.DBUpgrade;
import com.iscreen.dbupgrade.objects.Command;
import com.iscreen.dbupgrade.objects.FieldType;
import com.iscreen.dbupgrade.objects.NewField;
import com.iscreen.dbupgrade.objects.NewRow;
import com.iscreen.dbupgrade.operations.UpgradeCommand;
import com.iscreen.dbupgrade.operations.UpgradeStep;
import com.iscreen.dbupgrade.queries.InsertValuesQuery;
import com.iscreen.dbupgrade.queries.QueryUtils;
import com.iscreen.dbupgrade.queries.UpgradeQuery;
import com.iscreen.utils.db.query.Query;
import com.iscreen.utils.exceptions.ExceptionUtils;
import com.iscreen.utils.exceptions.IScreenException;

public class InsertValuesCommand extends UpgradeCommand
{
	private Logger logger = Logger.getLogger(InsertValuesCommand.class);

	public InsertValuesCommand(Command command, UpgradeStep parentStep)
	{
		super(command, parentStep);
	}

	
	@Override
	protected UpgradeQuery getUpgradeQuery(Command command)
	{
		return queryFactory.createInsertValuesQuery(command.getInsertRows());
	}
	
	private Object getTypedFieldValue(String value, FieldType fieldType) throws IScreenException
	{
		switch (fieldType)
		{
		case BOOLEAN:
			return Boolean.parseBoolean(value);
		case DATE:
			try
			{
				return DateFormat.getDateInstance().parse(value);
			}
			catch (ParseException e)
			{
				//TODO set error code
				throw new IScreenException(e);
			}
		case DECIMAL:
			return new BigDecimal(value);
		case ID:
			return new BigInteger(value);
		case STRING:
			return value;
		case TIMESTAMP:
			try
			{
				return DateFormat.getDateInstance().parse(value);
			}
			catch (ParseException e)
			{
				//TODO set error code
				throw new IScreenException(e);
			}
			
		}
		return value;
	}
	private void setParameters(Query query, Map<String, FieldType> fieldTypes, NewRow row) throws IScreenException
	{
		int index = 1;
		for (String fieldValue :  row.getFieldValue())
		{
			String fieldName = ((InsertValuesQuery) upgradeQuery).getFieldNames().get(index - 1);
			FieldType fieldType = fieldTypes.get(fieldName);
			Object typedValue = getTypedFieldValue(fieldValue, fieldType);
			query.setParameter(index++, typedValue);
		}
	}

	private void executeQuery(String tableName, List<NewRow> rows) throws IScreenException
	{
		Query query = connection.createQuery(upgradeQuery.getSql());
		Map<String, FieldType> fieldTypes = getFieldTypes();
		try
		{
			for (NewRow row : rows)
			{
				executeInsertRowQuery(tableName, query, fieldTypes, row);
			}
		}
		finally
		{
			query.close();
		}
	}


	private Map<String, FieldType> getFieldTypes() throws IScreenException
	{
		Map<String, FieldType> fieldTypes = new HashMap<String, FieldType>();
		InsertValuesQuery query = (InsertValuesQuery) upgradeQuery;

		List<NewField> fields = getFields(query);
		List<String> fieldNames = query.getFieldNames();
		
		for (NewField field : fields)
		{
			if (fieldNames.contains(field.getName()))
			{
				fieldTypes.put(field.getName(), field.getType());				
			}
		}
		return fieldTypes;
	}


	private List<NewField> getFields(InsertValuesQuery query) throws IScreenException
	{
		QueryUtils utils = DBUpgrade.getInstance().getFactory().getQueryUtils();
		return utils.getFields(query.getTableName());
	}


	private void executeInsertRowQuery(String tableName, Query query, Map<String, FieldType> fieldTypes, NewRow row)
			throws IScreenException
	{
		try
		{
			setParameters(query, fieldTypes, row);
			query.execute(false);
			logger.info("Inserted values to " + tableName + ". Field values: " + row.getFieldValue());
		}
		catch (IScreenException e)
		{
			if (ExceptionUtils.isDBDuplicateEntry(e))
			{
				logger.info("Record already exists in " + tableName + ". Field values: " + row.getFieldValue());
			}
			else
			{
				throw e;
			}
		}
	}
	
	@Override
	protected void doExecute() throws IScreenException
	{
		InsertValuesQuery insertValuesQuery = (InsertValuesQuery) upgradeQuery; 
		String tableName = insertValuesQuery.getTableName();
		List<NewRow> rows = insertValuesQuery.getRows();
		executeQuery(tableName, rows);
		
	}

	@Override
	public String getOperation()
	{
		return "Insert values to "
		+ ((InsertValuesQuery) upgradeQuery).getTableName();
	}

}
